package com.sinai.mshab.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sinai.mshab.client.util.Util;
import com.sinai.mshab.client.ui.Key;
import com.sinai.mshab.client.ui.aggregate.FormData;

public class FileDownloadServlet extends HttpServlet {

	private static final int FILE_READ_BUFFER_LIMIT = 256;

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		doPost(request, response);
	}

	/**
	 * We expect the following parameters: the key, fileNameFieldName,
	 * fileNameTableName, fileFieldName, fileTableName.
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {

		// get the required DB information.
		String fileNameFieldName = (String) request
				.getParameter("fileNameFieldName");
		String fileNameTableName = (String) request
				.getParameter("fileNameTableName");
		String fileFieldName = (String) request.getParameter("fileFieldName");
		String fileTableName = (String) request.getParameter("fileTableName");

		Key dbKeys = parseKeys(request, fileTableName);

		// tell the server to put the file on the server's hard drive.
		String pathToFile = getFile(fileNameFieldName, fileNameTableName,
				fileFieldName, fileTableName, dbKeys);

		// set the file's mimetype.
		// response.setContentType("application/pdf");
		response.setHeader("Content-Disposition", "inline; filename="
				+ Util.parseFileName(pathToFile));

		// open the file.
		File file = new File(pathToFile);
		FileInputStream fileStream = new FileInputStream(file);

		// read a certain number of bytes and write it to the response.
		ServletOutputStream out = response.getOutputStream();
		byte[] fileChunk = new byte[FILE_READ_BUFFER_LIMIT];
		while (fileStream.read(fileChunk) > 0) {
			out.write(fileChunk);
		}

		// flush the output.
		out.flush();
		fileStream.close();
	}

	/**
	 * Returns the path to where the file, retrieved from fileFieldName and
	 * fileFieldTable, was downloaded to on the file system.
	 * 
	 * @param fileNameFieldName
	 *            the field name holding the name of the file.
	 * @param fileNameTableName
	 *            the table name where the fileNameFieldName is stored.
	 * @param fileFieldName
	 *            the field name holding the binary file.
	 * @param fileTableName
	 *            the tabel name where fileFieldName is stored.
	 * @param dbKeys
	 *            the Key representing which record we want.
	 * @return the path to where the file, retrieved from fileFieldName and
	 *         fileFieldTable, was downloaded to on the file system.
	 */
	public String getFile(String fileNameFieldName, String fileNameTableName,
			String fileFieldName, String fileTableName, Key dbKeys) {

		// get the connection.
		Connection conn = Server.getConn();

		// construct the SQL for getting the binary file.
		String retrieveFileSQL = SQLUtils.constructSelectSQL(fileFieldName,
				fileTableName, dbKeys);

		// construct the SQL for getting the file's name.
		String retrieveFileNameSQL = SQLUtils.constructSelectSQL(
				fileNameFieldName, fileNameTableName, dbKeys);
		String result = null;

		try {
			ResultSet file = conn.createStatement().executeQuery(
					retrieveFileSQL);
			ResultSet fileName = conn.createStatement().executeQuery(
					retrieveFileNameSQL);
			if (file.next() && fileName.next()) {
				byte[] blob = file.getBytes(1);

				// use the /tmp directory and create a unique directory to avoid
				// overwrites when concurrently used.
				String path = "/tmp/" + (new Date()).getTime() + "/";
				Runtime.getRuntime().exec("mkdir " + path);

				// save the file path and return it.
				result = path + Util.parseFileName(fileName.getString(1));

				// save the file on the file system.
				File ioFile = new File(result);
				FileOutputStream fileStream = new FileOutputStream(ioFile);
				fileStream.write(blob);
			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return result;
	}

	private Key parseKeys(HttpServletRequest request, String tableName) {

		Key result = new Key();

		Enumeration enumerator = request.getParameterNames();
		while (enumerator.hasMoreElements()) {
			String param = (String) enumerator.nextElement();
			if (param.toString().startsWith("cond")) {
				String key = param.substring("cond".length());
				String value = request.getParameter(param);
				result.add(key, value, tableName);
			}
		}

		return result;
	}
}
